/*
 * The MIT License
 *
 * Copyright 2014 David Rodenas Pico.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package edu.uoc.sd.fr14.impl;

import static edu.uoc.sd.fr14.test.ServerStateCreator.CUSTOMER1000;
import static edu.uoc.sd.fr14.test.ServerStateCreator.CUSTOMER1001;
import static edu.uoc.sd.fr14.test.ServerStateCreator.FLIGHT_ALMOST_FULL;
import static edu.uoc.sd.fr14.test.ServerStateCreator.FLIGHT_CUSTOMER1000;
import static edu.uoc.sd.fr14.test.ServerStateCreator.FLIGHT_EMPTY;
import static edu.uoc.sd.fr14.test.ServerStateCreator.FLIGHT_FULL;
import static edu.uoc.sd.fr14.test.ServerStateCreator.FLIGHT_FULL_CUSTOMER1000;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import edu.uoc.sd.fr14.model.CustomerId;
import edu.uoc.sd.fr14.model.FlightId;
import edu.uoc.sd.fr14.model.Nseq;
import edu.uoc.sd.fr14.model.OpId;
import edu.uoc.sd.fr14.model.ServerId;
import edu.uoc.sd.fr14.model.SubLog;
import edu.uoc.sd.fr14.test.ServerTest;

/**
 * Test the server implementation assuming that there is only one single machine.
 * No replica, no network failures, and no leader.
 * 
 * @author drodenas
 */
public class ServerImplPh0 extends ServerImplCommon {
    
    public ServerImplPh0() {
    }
    
    @BeforeClass
    public static void setUpClass() {
    }
    
    @AfterClass
    public static void tearDownClass() {
    }

    @Before
    @Override
    public void setUp() {
        super.setUp();
    }
    
    @After
    @Override
    public void tearDown() {
        super.tearDown();
    }


    
    private void confirm(final FlightId flightId, final CustomerId customerId, final boolean expected) {
        
        ensureCallCustomerConfirmed(flightId, customerId, expected);
        server.confirm(flightId, customerId);
                
        assertEquals("company flight", expected, company.getFlight(flightId).confirm(customerId));
        testCalls.check();
    }

    @Test
    public void studentServerShouldExists() {
        // if not exists, it fails
    }

    @Test
    public void confirmFalseInEmptyFlights() {
        confirm(FLIGHT_EMPTY, CUSTOMER1000, false);
    }

    @Test
    public void confirmFalseInFullFlightsWithDifferentCustomers() {
        confirm(FLIGHT_FULL, CUSTOMER1000, false);
    }

    @Test
    public void confirmTrueInFlightsWithTheCustomer() {
        confirm(FLIGHT_CUSTOMER1000, CUSTOMER1000, true);
    }
    
    @Test
    public void confirmTrueInFullFlightsWithTheCustomer() {
        confirm(FLIGHT_FULL_CUSTOMER1000, CUSTOMER1000, true);
    }
    

    private void query(final FlightId flightId, final int expected) {
        
        final CustomerId customerId = CUSTOMER1001;
        ensureCallCustomerQueried(flightId, customerId, expected);
        server.query(flightId, customerId);
        
        assertEquals("company flight", expected, company.getFlight(flightId).query());
        testCalls.check();
    }

    @Test
    public void queryInFullFlights() {
        query(FLIGHT_FULL, 0);
    }

    @Test
    public void queryInAlmostFullFlights() {
        query(FLIGHT_ALMOST_FULL, 1);
    }
    
    
    private void seatConfirms(final FlightId flightId, final CustomerId customerId, final boolean expected) {
        makeMeLeader();
        
        final ServerTest serverTest = new ServerTest() {
            private int count;
            
            @Override
            public void prepare(OpId op, ServerId from) {
                assertTrue("call Cluster.others.prepare first", count <= 1);
                
                if (++count == 2) {
                    // emulate message back
                    server.prepared(op);
                }
            }

            @Override
            public void apply(Nseq nseq, SubLog ops, ServerId from) {
                assertTrue("Cluster.others.apply prepare messages should be sent before apply", count == 2);
            }
        };
        // let T1 work with T0
        cluster.quorum(2).server(LOWER, serverTest).server(OTHER, serverTest);
        
        // call to confirmed should be done always
        ensureCallCustomerConfirmed(flightId, customerId, expected);
        
        //////////////////////////////////
        server.seat(flightId, customerId, customerId);
        //////////////////////////////////
        
        assertEquals("company flight", expected, company.getFlight(flightId).confirm(customerId));
        testCalls.check();
    }

    @Test
    public void seatInEmptyFlightsConfirmsTrue() {
        seatConfirms(FLIGHT_EMPTY, CUSTOMER1000, true);
    }
    
    @Test
    public void seatInAlmostFullFlightsConfirmsTrue() {
        seatConfirms(FLIGHT_ALMOST_FULL, CUSTOMER1000, true);
    }
    
    @Test
    public void seatInFullFlightsConfirmsFalse() {
        seatConfirms(FLIGHT_FULL, CUSTOMER1000, false);
    }
    
    @Test
    public void seatInFlightsWithTheCustomerConfirmsTrue() {
        seatConfirms(FLIGHT_CUSTOMER1000, CUSTOMER1000, true);
    }
        
    @Test
    public void seatInFullFlightsWithTheCustomerConfirmsTrue() {
        seatConfirms(FLIGHT_FULL_CUSTOMER1000, CUSTOMER1000, true);
    }   
    
    
    
    
    
}
